/*
* Copyright (c) 2010 Michael Collins
*
* This file is part of TerraFirma.
*
* TerraFirma is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TerraFirma is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TerraFirma.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "input_thread.h"
#include "logic_thread.h"
#include "network_thread.h"
#include "io_service_thread.h"
#include <boost/thread/thread.hpp>
#include <boost/ref.hpp>
#include <cstdio>
#include <iostream>
#include <fstream>

#include "context.h"
#include "config.h"

//engine::mpi MPI; //The message-passing interface
#ifdef TF_INPUT
void create_dflt_rules(engine::input_thread& control)
{
    //Create the normal bindings
    control.add_boolean_state(8); //Create 8 boolean states
    control.add_2d_state(2); //Create 2 2D states

    sf::Event ev;
    //Bind left mouse button to FIRE
    ev.Type = sf::Event::MouseButtonPressed;
    ev.MouseButton.Button = sf::Mouse::Left;
    if(!control.add_control_rule("FIRE", 0, engine::input_thread::BUTN_MOM, 1, ev))
        printf("Failure to bind FIRE");

    //Bind right mouse button to ALT FIRE
    ev.MouseButton.Button = sf::Mouse::Right;
    if(!control.add_control_rule("ALT FIRE", 1, engine::input_thread::BUTN_MOM, 1, ev))
        printf("Failure to bind ALT FIRE");

    //Bind wheel up to PREV WEAPON
    ev.Type = sf::Event::MouseWheelMoved;
    ev.MouseWheel.Delta = 1;
    if(!control.add_control_rule("PREV WEAPON", 2, engine::input_thread::BUTN_MOM, 1, ev))
        printf("Failure to bind PREV WEAPON");
    //And wheel down to NEXT WEAPON
    ev.MouseWheel.Delta = -1;
    if(!control.add_control_rule("NEXT WEAPON", 3, engine::input_thread::BUTN_MOM, 1, ev))
        printf("Failure to bind NEXT WEAPON");

    //Bind WSAD to FORWARD, BACK, LEFT, and RIGHT respectively
    ev.Type = sf::Event::KeyPressed;
    ev.Key.Code = sf::Key::W;
    if(!control.add_control_rule("MOVE FORWARD", 8, engine::input_thread::BUTN_UP, 1, ev))
        printf("Failure to bind MOVE FORWARD");
    ev.Key.Code = sf::Key::S;
    if(!control.add_control_rule("MOVE BACK", 8, engine::input_thread::BUTN_DWN, 1, ev))
        printf("Failure to bind MOVE BACK");
    ev.Key.Code = sf::Key::A;
    if(!control.add_control_rule("STRAFE LEFT", 8, engine::input_thread::BUTN_LFT, 1, ev))
        printf("failure to bind STRAFE LEFT");
    ev.Key.Code = sf::Key::D;
    if(!control.add_control_rule("STRAFE RIGHT", 8, engine::input_thread::BUTN_RGT, 1, ev))
        printf("Failure to bind STRAFE RIGHT");

    //Bind jump and crouch and run
    ev.Key.Code = sf::Key::Space;
    if(!control.add_control_rule("JUMP", 4, engine::input_thread::BUTN_MOM, 1, ev))
        printf("Failure to bind JUMP");
    ev.Key.Code = sf::Key::LControl;
    if(!control.add_control_rule("CROUCH", 5, engine::input_thread::BUTN_MOM, 1, ev))
        printf("Failure to bind CROUCH");
    ev.Key.Code = sf::Key::LShift;
    if(!control.add_control_rule("SPRINT", 6, engine::input_thread::BUTN_MOM, 1, ev))
        printf("Failure to bind SPRINT");
    //Bind "USE" to 'e'
    ev.Key.Code = sf::Key::E;
    if(!control.add_control_rule("OPEN/USE", 7, engine::input_thread::BUTN_MOM, 1, ev))
        printf("Failure to bind OPEN/USE");

    //Write this crap to the file so we don't have to do it again next time we run
    std::ofstream file("./cfg/control_rules.xml", std::ios_base::out | std::ios_base::trunc);
    if(file.good()) {
        boost::archive::xml_oarchive xml_file(file);
        control.save_fixed(xml_file);
    }
    file.close();

    //Since we had to re-load the default rules, we should restore default bindings as well
    file.open("./cfg/control_binding.xml", std::ios_base::out | std::ios_base::trunc);
    if(file.good()) {
        boost::archive::xml_oarchive xml_file(file);
        control.save_bindings(xml_file);
    }
}
#endif

int main(int argc, char** argv)
{
    context::Instance().config.set_default(); //Make a default config setup
    //Call all the threads and put them into a thread group, so we can block on all of their destruction
    boost::thread_group threads;

    #if defined(TF_INPUT)
    engine::input_thread control;
    #endif
    #if defined(TF_GAMESTATE)
    engine::logic_thread logic;
    #endif
    #if defined(TF_NETWORK)
    boost::asio::io_service io_service;
    engine::io_service_thread io_thread(io_service);
    #if defined(SERVING)
    engine::network_thread net(io_service, context::Instance().config.port); //Create server
    threads.create_thread(boost::ref(io_thread)); //Start the io_service run() sequence
    threads.create_thread(boost::ref(net));
    #else
    engine::network_thread net(io_service);
    if(!net.connect(context::Instance().config.server, context::Instance().config.port))
        return 1; //Error did not connect
    threads.create_thread(boost::ref(io_thread)); //Start the io_service run() sequence
    threads.create_thread(boost::ref(net)); //network needs to be fully up and running before it can handle messages from another task thread
    if(!logic.Client_connect_player())
        return 1; //Server did not accept our join request
    #endif
    #endif


    //MPI.add_listener((engine::task_thread<>)control);
    //Create the window
    #if defined(TF_GRAPHICS)
    context::Instance().main_window.Create(sf::VideoMode(800, 600, 32), "TerraFirma");
    context::Instance().main_window.EnableKeyRepeat(true); //Key repeats seem to generate key events as well
    #endif

    #if defined(TF_INPUT)
    //Try to open the xml file that describes the control rules
    std::ifstream rule_file("./cfg/control_rules.xml");
    std::ifstream bind_file("./cfg/control_binding.xml");
    if(rule_file.fail() || bind_file.fail())
        //The files don't exist. Add the default rules and bindings
        create_dflt_rules(control);
    else {
        boost::archive::xml_iarchive ctrl_rules(rule_file);
        boost::archive::xml_iarchive ctrl_binds(bind_file);
        control.load_fixed(ctrl_rules);
        control.load_bindings(ctrl_binds);
    }
    #endif

    //boost::thread control_thread(boost::ref(control));
    //threads.add_thread(&control_thread);
    #if defined(TF_INPUT)
    threads.create_thread(boost::ref(control));
    printf("Created control thread. tid = %x\n", control.tid);
    #endif
    #if defined(TF_GAMESTATE)
    threads.create_thread(boost::ref(logic));
    printf("Created logic thread. tid = %x\n", logic.tid);
    #endif
    //#if defined(TF_NETWORK)
    //threads.create_thread(boost::ref(net));
    printf("Created network thread. tid = %d\n", net.tid);
    //io_service.run();
    //#endif
    threads.join_all(); //This blocks the main thread unil all child threads have terminated (so the user quits and shit)

    //threads.remove_thread(&control_thread);
    return 0;
}
